package edu.gmu.client;

import edu.gmu.messages.EndPoint;
import edu.gmu.messages.ListManagerResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * RingIterator iterates over the Ring using specified endpoints as entry 
 * points into the ring.
 * 
 * @author James Pope
 */
public class RingIterator
{
    private List<EndPoint> entryEndpoints = null;
    
    private NamingServiceStub stub = null;
    private EndPoint startEndPoint = null;
    private EndPoint endPoint      = null;
    private boolean hasNext        = true;
    
    /**
     * Creates a new RingIterator.
     * @param ringIterator
     */
    public RingIterator( EndPoint next ) throws IOException
    {
        List<EndPoint> singleEndpoint = new ArrayList<EndPoint>();
        singleEndpoint.add(next);
        this.init(singleEndpoint);
    }
    
    /**
     * Creates a new NamingServiceStub.
     * @param nameSeverEndpoint
     */
    public RingIterator( List<EndPoint> entryEndpoints ) throws IOException
    {
        this.init(entryEndpoints);
    }
    
    private void init( List<EndPoint> entryEndpoints ) throws IOException
    {
        this.entryEndpoints = entryEndpoints;
        
        this.stub = this.getNamingServiceStub();
        if( stub == null )
        {
            String s = "Unable to get an entry point for NamingServiceStub.";
            throw new IOException(s);
        }
        
        this.startEndPoint = stub.getEndpoint();
        this.endPoint      = startEndPoint;
    }
    
    /**
     * Tries to get NamingServiceStub from list of entry points - returns
     * null if no entry point found;
     * @return stub or null
     */
    private NamingServiceStub getNamingServiceStub()
    {
        NamingServiceStub nsStub = null;
        for (int i = 0; i < entryEndpoints.size(); i++)
        {
            try
            {
                EndPoint nsEndPoint = entryEndpoints.get(i);
                //System.out.println( "Trying " + nsEndPoint );
                NamingServiceStub aStub = new NamingServiceStub(nsEndPoint);
                int num = aStub.echo(665);
                
                // If we get here - assume good entry point
                nsStub = aStub;
                //System.out.println( "   succeeded " + nsEndPoint );
                break;
            }
            catch( IOException ioe )
            {
                // ignore
            }
        }
        return nsStub;
    }
    
    public boolean hasNext()
    {
        return this.hasNext;
    }
    
    
    public ListManagerResponse next( ) throws IOException
    {
        if( this.hasNext == false )
        {
            // No more to iterate
            return null;
        }
        
        // Get new stub for each naming server
        stub = new NamingServiceStub( endPoint );
        //System.out.println( "In ring " + endPoint );
        /*
         * Be careful - NS service is single-threaded and may hang here.
         */
        ListManagerResponse resourceResponse = stub.getManagers();
        //System.out.println( "Out ring " + endPoint );
        //stub.stop(); // stop - though really not necessary, good practice
        
        // could be null, in which case we bale anyway
        endPoint = resourceResponse.next;
        
        if( startEndPoint.equals(endPoint) == true )
        {
            // No more to iterate
            this.hasNext = false;
        }
        
        //return stub;
        return resourceResponse;
    }
    
    
    //------------------------------------------------------------------------//
    
    /**
     * Test main
     * @param args
     * @throws java.io.IOException
     */
    public static void main( String[] args ) throws IOException
    {
        List<EndPoint> entryEndpoints = new ArrayList<EndPoint>();
        entryEndpoints.add( EndPoint.deriveHostAddress(1) );
        entryEndpoints.add( EndPoint.deriveHostAddress(3) );
        entryEndpoints.add( EndPoint.deriveHostAddress(5) );
        
        RingIterator ringIter = new RingIterator( entryEndpoints );
        
        while( ringIter.hasNext() )
        {
            System.out.println( ringIter.next().localhost );
        }
    }
}